<!DOCTYPE html>





<html lang="zh-CN">
<head>
  <!-- hexo-inject:begin --><!-- hexo-inject:end --><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/sunpages/images/apple-touch-icon-next.png?v=7.4.0">
  <link rel="icon" type="image/png" sizes="32x32" href="/sunpages/images/favicon-32x32-next.png?v=7.4.0">
  <link rel="icon" type="image/png" sizes="16x16" href="/sunpages/images/favicon-16x16-next.png?v=7.4.0">
  <link rel="mask-icon" href="/sunpages/images/logo.svg?v=7.4.0" color="#222">

<link rel="stylesheet" href="/sunpages/css/main.css?v=7.4.0">


<link rel="stylesheet" href="/sunpages/lib/font-awesome/css/font-awesome.min.css?v=4.7.0">


<script id="hexo-configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/sunpages/',
    scheme: 'Pisces',
    version: '7.4.0',
    exturl: false,
    sidebar: {"position":"left","display":"post","offset":12,"onmobile":false},
    copycode: {"enable":true,"show_result":false,"style":null},
    back2top: {"enable":true,"sidebar":false,"scrollpercent":true},
    bookmark: {"enable":false,"color":"#222","save":"auto"},
    fancybox: false,
    mediumzoom: false,
    lazyload: false,
    pangu: false,
    algolia: {
      appID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    },
    localsearch: {"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
    path: 'search.xml',
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    translation: {
      copy_button: '复制',
      copy_success: '复制成功',
      copy_failure: '复制失败'
    },
    sidebarPadding: 40
  };
</script>

  <meta name="description" content="实验三 实验报告 孙汉武    安全1601    16281047">
<meta property="og:type" content="article">
<meta property="og:title" content="进程实验二">
<meta property="og:url" content="https://yuandaima1.gitee.io/sunpages/2019/03/21/lab3/index.html">
<meta property="og:site_name" content="SunPages">
<meta property="og:description" content="实验三 实验报告 孙汉武    安全1601    16281047">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-172743.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173757.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173803.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173808.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173812.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173819.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173823.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173828.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173833.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173838.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173844.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173849.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173854.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173858.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173903.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173913.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173917.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173920.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173932.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173937.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173942.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173945.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173951.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173955.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174009.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174012.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174019.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174023.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174026.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174030.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174035.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174039.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174043.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174046.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174053.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174058.jpg">
<meta property="og:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174105.jpg">
<meta property="article:published_time" content="2019-03-21T12:10:33.000Z">
<meta property="article:modified_time" content="2019-09-12T09:34:23.000Z">
<meta property="article:author" content="sunhanwu">
<meta property="article:tag" content="操作系统">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-172743.jpg">
  <link rel="canonical" href="https://yuandaima1.gitee.io/sunpages/2019/03/21/lab3/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome: false,
    isPost: true,
    isPage: false,
    isArchive: false
  };
</script>

  <title>进程实验二 | SunPages</title>
  








  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .logo,
  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript><!-- hexo-inject:begin --><!-- hexo-inject:end -->

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">
  <!-- hexo-inject:begin --><!-- hexo-inject:end --><div class="container use-motion">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-meta">

    <div>
      <a href="/sunpages/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">SunPages</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <h1 class="site-subtitle" itemprop="description">屁话少说,放码过来！</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
      
      
      
        
        <li class="menu-item menu-item-home">
      
    

    <a href="/sunpages/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-about">
      
    

    <a href="/sunpages/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i> <br>关于</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-tags">
      
    

    <a href="/sunpages/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i> <br>标签</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-categories">
      
    

    <a href="/sunpages/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i> <br>分类</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-archives">
      
    

    <a href="/sunpages/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i> <br>归档</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-photos">
      
    

    <a href="/sunpages/photo/" rel="section"><i class="menu-item-icon fa fa-fw fa-image"></i> <br>Photos</a>

  </li>
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger">
        
          <i class="menu-item-icon fa fa-search fa-fw"></i> <br>搜索</a>
      </li>
    
  </ul>

</nav>
  <div class="site-search">
    <div class="popup search-popup">
    <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input" id="search-input"></div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="algolia-results">
  <div id="algolia-stats"></div>
  <div id="algolia-hits"></div>
  <div id="algolia-pagination" class="algolia-pagination"></div>
</div>

  
</div>
<div class="search-pop-overlay"></div>

  </div>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/sunhanwu" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
            

          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
      <article itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block post">
    <link itemprop="mainEntityOfPage" href="https://yuandaima1.gitee.io/sunpages/sunpages/2019/03/21/lab3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="sunhanwu">
      <meta itemprop="description" content="Talk is cheap, show me the code!">
      <meta itemprop="image" content="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-09-12-logo.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="SunPages">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">进程实验二

          
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              
                
              

              <time title="创建时间：2019-03-21 20:10:33" itemprop="dateCreated datePublished" datetime="2019-03-21T20:10:33+08:00">2019-03-21</time>
            </span>
          
            

            
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2019-09-12 17:34:23" itemprop="dateModified" datetime="2019-09-12T17:34:23+08:00">2019-09-12</time>
              </span>
            
          
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/sunpages/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%AE%9E%E9%AA%8C/" itemprop="url" rel="index"><span itemprop="name">操作系统实验</span></a></span>

                
                
              
            </span>
          

          
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="fa fa-comment-o"></i>
      </span>
        
      
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/sunpages/2019/03/21/lab3/#comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/sunpages/2019/03/21/lab3/" itemprop="commentCount"></span></a>
  </span>
  
  
          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="fa fa-file-word-o"></i>
              </span>
              
                <span class="post-meta-item-text">本文字数：</span>
              
              <span>41k</span>
            </span>
          
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="fa fa-clock-o"></i>
              </span>
              
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              
              <span>37 分钟</span>
            </span>
          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="实验三-实验报告"><a href="#实验三-实验报告" class="headerlink" title="实验三 实验报告"></a><center>实验三 实验报告</center></h1><blockquote>
<center>孙汉武    安全1601    16281047</center>

</blockquote><a id="more"></a>
<p><a href="https://github.com/sunhanwu/16281047_OperatingSystemExperiment/tree/master/lab3" target="_blank" rel="noopener">实验源代码链接</a></p>
<h2 id="Task-1"><a href="#Task-1" class="headerlink" title="Task 1"></a>Task 1</h2><h3 id="1-1-实验要求"><a href="#1-1-实验要求" class="headerlink" title="1.1 实验要求"></a>1.1 实验要求</h3><p>$\qquad$通过fork的方式，产生4个进程P1,P2,P3,P4，每个进程打印输出自己的名字，例如P1输出“I am the process P1”。要求P1最先执行，P2、P3互斥执行，P4最后执行。通过多次测试验证实现是否正确。</p>
<h3 id="1-2-实验过程"><a href="#1-2-实验过程" class="headerlink" title="1.2 实验过程"></a>1.2 实验过程</h3><ol>
<li><p>实验源码</p>
<p><code>Task1.c</code> </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">sem_t</span> *P1_signal,*P2_signal,*P3_signal;</span><br><span class="line">	<span class="comment">//主函数中的进程是P1</span></span><br><span class="line">	<span class="keyword">pid_t</span> p2,p3,p4; </span><br><span class="line">	P1_signal=sem_open(<span class="string">"P1_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line">	P2_signal=sem_open(<span class="string">"P2_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line">	P3_signal=sem_open(<span class="string">"P3_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">	p2=fork();<span class="comment">//创建进程P2</span></span><br><span class="line">	<span class="keyword">if</span>(p2&lt;<span class="number">0</span>)	</span><br><span class="line">	&#123;</span><br><span class="line">		perror(<span class="string">"创建进程p2出错！"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span>(p2==<span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		sem_wait(P1_signal);</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">"I am the process P2!\n"</span>);</span><br><span class="line">		sem_post(P1_signal);</span><br><span class="line">		sem_post(P2_signal);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span>(p2&gt;<span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		p3=fork();</span><br><span class="line">		<span class="keyword">if</span>(p3&lt;<span class="number">0</span>)	</span><br><span class="line">		&#123;</span><br><span class="line">			perror(<span class="string">"创建进程p出错！"</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span>(p3==<span class="number">0</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			sem_wait(P1_signal);</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"I am the process P3!\n"</span>);</span><br><span class="line">			sem_post(P1_signal);</span><br><span class="line">			sem_post(P3_signal);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span>(p3&gt;<span class="number">0</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"I am the process P1!\n"</span>);</span><br><span class="line">			sem_post(P1_signal);</span><br><span class="line">			p4=fork();</span><br><span class="line">			<span class="keyword">if</span>(p4&lt;<span class="number">0</span>)	</span><br><span class="line">			&#123;</span><br><span class="line">				perror(<span class="string">"创建进程p4出错！"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span>(p4==<span class="number">0</span>)</span><br><span class="line">			&#123;</span><br><span class="line">				sem_wait(P2_signal);</span><br><span class="line">				sem_wait(P3_signal);</span><br><span class="line">				<span class="built_in">printf</span>(<span class="string">"I am the process P4!\n"</span>);</span><br><span class="line">				sem_post(P2_signal);</span><br><span class="line">				sem_post(P3_signal);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	sem_close(P1_signal);</span><br><span class="line">	sem_close(P3_signal);</span><br><span class="line">	sem_close(P2_signal);</span><br><span class="line">	sem_unlink(<span class="string">"P1_signal"</span>);</span><br><span class="line">	sem_unlink(<span class="string">"P2_signal"</span>);</span><br><span class="line">	sem_unlink(<span class="string">"P3_signal"</span>);</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>原理解释</p>
<ul>
<li><p>前趋图</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-172743.jpg" alt></p>
</li>
</ul>
</li>
</ol>
<pre><code> 前驱关系：`P1--&gt;P2`、`P1--&gt;P3`、`P2--&gt;P4`、`P3--&gt;P4`
</code></pre><ul>
<li><p>前驱关系实现</p>
<p>题目要求产生的四个进程必须是P1最先执行，P2、P3在P1执行完后互斥执行，P4最后执行。于是根据要求有了上面的前驱关系和前驱图。但是如何实现这种进程间的前驱关系呢？比较自然的想到了是用信号量机制。如上面的代码所示，定义了三个信号量，P1_signal、P2_signal和P3_signal，其初值均为0</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">P1_signal=sem_open(<span class="string">"P1_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line">   P2_signal=sem_open(<span class="string">"P2_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line">   P3_signal=sem_open(<span class="string">"P3_signal"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br></pre></td></tr></table></figure>
</li>
</ul>
<pre><code> P1进程执行完打印任务之后对P1_signal信号量进行V操作，产生一个资源让等待P1_signal的进程P2和P3其中之一可以执行。由于P2和P3都是等待P1_signal信号量，但是P1进程只产生一个单位的信号，所以P2和P3的执行是互斥的，这样就满足了题目要求。最后在P2和P3执行完打印任务后对信号量P2_signal和P3_signal进行V操作从各产生一个单位的信号量，而进程P4会等待P2_signal和P3_signal，所以知道当P2和P3进程都完成才能进行P4进程。通过控制这三个信号量，这四个进程之间的前驱关系就满足了题目要求。

 ![](http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173752.jpg)
</code></pre><ul>
<li><p>进程产生实现</p>
<p>根据题目要求，通过fork的方式产生四个进程。fork函数会从当前位置复制进程，并且在父进程中返回的pid为复制进程的真实pid，在子进程中返回的pid为0。了解这些知识之后可以得到如下的流程图：</p>
<p>下面的流程图仅表示进程间的关系，前驱关系的实现请看上一小节.</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173757.jpg" alt></p>
</li>
<li><p>进程树</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173803.jpg" alt></p>
</li>
</ul>
<ol>
<li><p>编译源码</p>
<p>通过下面的命令编译源码，得到可执行程序</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -g task1.c -o task1 -lpthrea</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="1-3-实验结果"><a href="#1-3-实验结果" class="headerlink" title="1.3 实验结果"></a>1.3 实验结果</h3><p>$\qquad$通过上面的实验已经得到满足实验要求的可执行程序task1,下面给出运行结果，经过多次测试，四个进程在屏幕上打印的顺序只有两种结果，分别如下：</p>
<ol>
<li><p>顺序1：<code>P1--&gt;P2--&gt;P3--&gt;P4</code></p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173808.jpg" alt></p>
</li>
<li><p>顺序2：<code>P1--&gt;P3--P2--&gt;P4</code></p>
</li>
</ol>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173812.jpg" alt></p>
<h3 id="1-4-现象解释"><a href="#1-4-现象解释" class="headerlink" title="1.4 现象解释"></a>1.4 现象解释</h3><p>$\qquad$测试的实验结果中出现两种执行顺序，通过1.2 节中的分析不难解释这种现象，由于P1是P2和P3的前驱，所以P1一定会在P2和P3之前执行，但是P2和P3是互斥关系，这两个进程谁先获得P1产生的信号量谁就先执行另一个进程等待。最后等P2和P3都执行完了再执行P4，所以会出现上面的两种执行顺序。</p>
<h2 id="Task-2"><a href="#Task-2" class="headerlink" title="Task 2"></a>Task 2</h2><h3 id="2-1-实验要求"><a href="#2-1-实验要求" class="headerlink" title="2.1 实验要求"></a>2.1 实验要求</h3><p>$\qquad$火车票余票数ticketCount 初始值为1000，有一个售票线程，一个退票线程，各循环执行多次。添加同步机制，使得结果始终正确。要求多次测试添加同步机制前后的实验效果。</p>
<h3 id="2-2-实验过程"><a href="#2-2-实验过程" class="headerlink" title="2.2 实验过程"></a>2.2 实验过程</h3><h4 id="2-2-1未添加同步机制"><a href="#2-2-1未添加同步机制" class="headerlink" title="2.2.1未添加同步机制"></a>2.2.1未添加同步机制</h4><ol>
<li><p>实验源码</p>
<p><code>task2_1.c</code>:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="keyword">int</span> ticketCount=<span class="number">1000</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">SaleThread</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> num,temp;</span><br><span class="line">	num=atoi(arg);</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;num;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span>(i % <span class="number">10</span> ==<span class="number">0</span>)</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"卖%d张票,剩余%d张票\n"</span>,i,ticketCount);</span><br><span class="line">		temp=ticketCount;</span><br><span class="line">		<span class="comment">//放弃CPU，强制切换到另外一个进程</span></span><br><span class="line">		pthread_yield();</span><br><span class="line">		temp=temp<span class="number">-1</span>;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		ticketCount=temp;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">RefundThread</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> num,temp;</span><br><span class="line">	num=atoi(arg);</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;num;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span>(i % <span class="number">10</span> ==<span class="number">0</span>)</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"退%d张票，剩余%d张票\n"</span>,i,ticketCount);</span><br><span class="line">		temp=ticketCount;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		temp=temp+<span class="number">1</span>;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		ticketCount=temp;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">if</span>(argc!=<span class="number">3</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">"请正确输入参数！\n"</span>);</span><br><span class="line">		<span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">"初始票数为：%d\n"</span>,ticketCount);</span><br><span class="line">	<span class="keyword">pthread_t</span> p1,p2;</span><br><span class="line">	<span class="comment">/* printf("%s %s",argv[1],argv[2]); */</span></span><br><span class="line">	pthread_create(&amp;p1,<span class="literal">NULL</span>,SaleThread,argv[<span class="number">1</span>]);</span><br><span class="line">	pthread_create(&amp;p2,<span class="literal">NULL</span>,RefundThread,argv[<span class="number">2</span>]);</span><br><span class="line">	pthread_join(p1,<span class="literal">NULL</span>);</span><br><span class="line">	pthread_join(p2,<span class="literal">NULL</span>);</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">"最终票数为：%d\n"</span>,ticketCount);</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li>在main函数中创建两个线程，分别是模拟售票的线程<code>SaleThread</code>和模拟退票的线程<code>RefundThread</code>，两个进程并发执行，不添加任何的同步机制。</li>
<li>模拟票数的变量ticketCount是全局变量</li>
<li>程序运行需要输入两个参数，第一个是售票数量，第二个数退票数量</li>
</ul>
</li>
<li><p>程序运行结果</p>
<p>编译上述程序，得到可执行程序<code>task2_1</code></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -g  task2_1.c -o task2_1 -lpthread</span><br></pre></td></tr></table></figure>
<p>多次测试运行，运行结果可以分为两种类型，一种是售票数量比退票数量多，另一种是售票数量比退票数量少。两种情况的结果分别如下：</p>
<ul>
<li><p>售票数量比退票数量多：</p>
<p>初始票数：1000      售票：100     退票：40</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173819.jpg" alt></p>
</li>
</ul>
</li>
</ol>
<ul>
<li><p>售票数量比退票数量少：</p>
<p>初始票数：1000    售票：50    退票：80</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173823.jpg" alt></p>
</li>
</ul>
<ol>
<li><p>实验现象归纳</p>
<p>通过一系列的测试，归纳出的实现现象如下：</p>
<ul>
<li>当售票数量大于退票数量的时候，最终票数等于总票数减去售票数</li>
<li>当售票数量小于退票数量的时候，最终票数等于总票数加上退票数</li>
</ul>
</li>
</ol>
<h4 id="2-2-2-添加同步机制"><a href="#2-2-2-添加同步机制" class="headerlink" title="2.2.2 添加同步机制"></a>2.2.2 添加同步机制</h4><ol>
<li><p>实验源码</p>
<p><code>task2_2.c</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="keyword">volatile</span> <span class="keyword">int</span> ticketCount=<span class="number">1000</span>;</span><br><span class="line"><span class="keyword">sem_t</span> *flag=<span class="literal">NULL</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">SaleThread</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> num,temp;</span><br><span class="line">	num=atoi(arg);</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;num;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span>(i % <span class="number">10</span> ==<span class="number">0</span>)</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"卖%d张票,剩余%d张票\n"</span>,i,ticketCount);</span><br><span class="line">		sem_wait(flag);</span><br><span class="line">		temp=ticketCount;</span><br><span class="line">		<span class="comment">//放弃CPU，强制切换到另外一个进程</span></span><br><span class="line">		pthread_yield();</span><br><span class="line">		temp=temp<span class="number">-1</span>;</span><br><span class="line">		ticketCount-=<span class="number">1</span>;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		ticketCount=temp;</span><br><span class="line">		sem_post(flag);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">RefundThread</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> num,temp;</span><br><span class="line">	num=atoi(arg);</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;num;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span>(i % <span class="number">10</span> ==<span class="number">0</span>)</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"退%d张票，剩余%d张票\n"</span>,i,ticketCount);</span><br><span class="line">		sem_wait(flag);</span><br><span class="line">		temp=ticketCount;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		temp=temp+<span class="number">1</span>;</span><br><span class="line">		ticketCount+=<span class="number">1</span>;</span><br><span class="line">		pthread_yield();</span><br><span class="line">		ticketCount=temp;</span><br><span class="line">		sem_post(flag);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">if</span>(argc!=<span class="number">3</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">"请正确输入参数！\n"</span>);</span><br><span class="line">		<span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	flag=sem_open(<span class="string">"flag"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">1</span>);</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">"初始票数为：%d\n"</span>,ticketCount);</span><br><span class="line">	<span class="keyword">pthread_t</span> p1,p2;</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">"%s %s"</span>,argv[<span class="number">1</span>],argv[<span class="number">2</span>]);</span><br><span class="line">	pthread_create(&amp;p1,<span class="literal">NULL</span>,SaleThread,argv[<span class="number">1</span>]);</span><br><span class="line">	pthread_create(&amp;p2,<span class="literal">NULL</span>,RefundThread,argv[<span class="number">2</span>]);</span><br><span class="line">	pthread_join(p1,<span class="literal">NULL</span>);</span><br><span class="line">	pthread_join(p2,<span class="literal">NULL</span>);</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">"最终票数为：%d\n"</span>,ticketCount);</span><br><span class="line">	sem_close(flag);</span><br><span class="line">	sem_unlink(<span class="string">"flag"</span>);</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li><p>task2_2.c在task2_1.c的基础上增加了同步机制，其他部分完全一致，通过信号量flag的控制，让售票线程和退票线程一次只能执行一个，在一个没有执行完成之前另一个不能进入执行，这样就保证了售票操作和退票操作的原子性，避免了脏数据的读取。</p>
</li>
<li><p>flag初始值为设置为1，表示每次只允许一个线程操作ticketCount这个数据</p>
</li>
<li><p>售票线程和退票线程在进入操作之前都要sem_wait(flag)，等待信号量，在完成操作之后要sem_post(flag)，下图是售票线程中增加了信号量的操作：</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173828.jpg" alt></p>
</li>
</ul>
</li>
<li><p>程序运行结果</p>
<p>编译上述程序，得到可执行程序task2_2</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc task2_2.c -o task2_2 -lpthread</span><br></pre></td></tr></table></figure>
<p>多次测试运行，测试主要分为两种类型，一种是售票数量比退票数量多，另一种是售票数量比退票数量少。两种情况的结果分别如下：</p>
<ul>
<li><p>售票数量比退票数量多：</p>
<p>初始票数：1000      售票：100     退票：40</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173833.jpg" alt></p>
</li>
<li><p>退票数量比售票数量多：</p>
<p>初始票数：1000    售票：50    退票：80</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173838.jpg" alt></p>
</li>
</ul>
</li>
<li><p>实验现象归纳</p>
<ul>
<li><p>在第一个测试样例中，初始票数为1000，售票100并且退票40，最终总票数为940，结果正确；</p>
</li>
<li><p>在第二个测试样例中，初始票数为1000，售票50并且退票80，最终总票数为1030，结果正确。</p>
</li>
</ul>
<ul>
<li><p>通过上面的测试结果可以看出，不论是售票数量比退票数量多还是少，都不会发生类似前面2.2.1的问题，最终的票数是期待得到的结果。</p>
</li>
<li><p>上面的实验证实了增加了同步机制之后的多线程并发程序有效的解决了脏数据的读取问题</p>
</li>
</ul>
</li>
</ol>
<h3 id="2-3-实验结果"><a href="#2-3-实验结果" class="headerlink" title="2.3 实验结果"></a>2.3 实验结果</h3><p> 通过2.2节的对比实验可以看出，在执行多进程并发程序的时候，由于多进程的切换可能发生在某个进程的中间，会导致在一个进程处理的数据未写入ticketCount之前另外一个进程读取该数据，这样就导致了脏数据的读取，导致最终结果的不正确。</p>
<p>在2.2节的后半部分通过怎加同步机制，保证售票进程和退票进程的的原子性，就是指在某个进程操作的时候，在它完成操作之前另外一个进程无法操作共享变量ticketCount,这样就避免了脏数据的发生，得到了预期的正确结果。</p>
<h3 id="2-4-现象解释"><a href="#2-4-现象解释" class="headerlink" title="2.4 现象解释"></a>2.4 现象解释</h3><h4 id="2-4-1-现象解释1"><a href="#2-4-1-现象解释1" class="headerlink" title="2.4.1 现象解释1"></a>2.4.1 现象解释1</h4><p>在2.2.1节的实验中，以售票线程为例（代码如下图所示），没有添加同步机制，并且在进行<code>temp=temp-1</code>和<code>temp=ticketCount</code>的后面均加上了pthread_yield，这个函数的作用是放弃对CPU的使用权，切换到其他进程中，本实验中就是切换到退票进程中。</p>
<p>这样就能解释为什么2.2.1节中的实验现象，在2.2.1节中，不论售票数多还是退票数多，最终结果都是总票数加上或减去值比较大的那个数。通过分析可以得出解释，售票进程和退票进程同时进行，初始票数均为1000，售票进程完成一次是票数为999，售票进程开始下一次售票，但是在运行<code>temp=ticketCount</code>之前，退票进程处理的数据还没有写入到内存中，导致售票进程读取的还是自己之前计算的ticketCount值，而不是全局的值。退票进程也是同理。</p>
<p>但是为什么刚好就是总票数加上或减去值比较大的那个呢？按照道理来说因该售票进程执行temp=ticketCount在退票进程写入ticketCount值之前发生是存在一定概率的，但是在目前为止的所有测试结果全部都是在写入之前读取ticketCount值，对此的解释是由于ticketCount=temp和temp=ticketCount之间没有加pthread_yield操作，而现代的处理器运算速度足够快，在退票进程放弃CPU控制权的那个时间片已经完成了这两步操作，所以相当于售票进程读取的ticketCount一直是自己本身的值，退票进程处理的数据对售票进程并没有影响。</p>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173844.jpg" alt></p>
<p>为了验证上面的猜想，在=如下图所示代码，在ticketCount之后增加一行代码，pthread_yield，放弃当前进程对CPU的控制权，即售票进程放弃对CPU的控制权转而交给退票进程，这个时候退票进程处理的数据就能写入到内存中，而当售票进程再次处理temp=ticketCount的时候，读取的就是退票进程已经写入的数据。如果猜想正确的话，期待的最终票数因该还会发生错误，但并不是像第一中那种恰好等于总票数加减数值大的那个数。</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173849.jpg" alt></p>
<p>得到的结果如下，发现最终的票数不在是1000+50=1050，而是分布在1000~1050之间的数值。猜想得到验证。</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173854.jpg" alt></p>
<p>针对上面的猜想（CPU运算速度过快，导致ticketCount=temp和temp=ticketCount两步操作在一个进程的时间片内完成导致的数据错误），另外的一种验证方式是将初始票数和售票退票数设置的足够大，当数据足够大的时候，就会存在一定概率出现在一个进程的ticketCount=temp和temp=ticketCount两步操作之间切换进程的问题，得到的结果就不会类似2.2.1中的那样，而是类似在ticketCount=temp下面加了pthread_yield那样。</p>
 <div align="center">
     <img src width="600" div align="center"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173858.jpg" alt></p>
<p>再次运行，可以看到如下的实验结果：</p>
<div align="center">      <img src width="600" div align="center"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173903.jpg" alt></p>
<h4 id="2-4-2-现象解释2"><a href="#2-4-2-现象解释2" class="headerlink" title="2.4.2 现象解释2"></a>2.4.2 现象解释2</h4><p>在2.2.2节中，当给售票进程和退票进程都加上同步机制后，保证了每个线程操作的原子性，每个线程操作的过程中其他的线程不能对共享的ticketCount变量进程修改，这样的话最终的结果就是正确的结果。</p>
<h2 id="Task-3"><a href="#Task-3" class="headerlink" title="Task 3"></a>Task 3</h2><h3 id="3-1-实验要求"><a href="#3-1-实验要求" class="headerlink" title="3.1 实验要求"></a>3.1 实验要求</h3><p>$\qquad$一个生产者一个消费者线程同步。设置一个线程共享的缓冲区， char buf[10]。一个线程不断从键盘输入字符到buf,一个线程不断的把buf的内容输出到显示器。要求输出的和输入的字符和顺序完全一致。（在输出线程中，每次输出睡眠一秒钟，然后以不同的速度输入测试输出是否正确）。要求多次测试添加同步机制前后的实验效果。</p>
<h3 id="3-2-实验过程"><a href="#3-2-实验过程" class="headerlink" title="3.2 实验过程"></a>3.2 实验过程</h3><ol>
<li><p>实验源码</p>
<p><code>task3.c</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">10</span>];</span><br><span class="line"><span class="keyword">sem_t</span> *empty=<span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">sem_t</span> *full=<span class="literal">NULL</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">worker1</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		sem_wait(empty);</span><br><span class="line">		<span class="comment">/* fflush(stdin); */</span></span><br><span class="line">		<span class="comment">/* printf("输入："); */</span></span><br><span class="line">		<span class="built_in">scanf</span>(<span class="string">"%c"</span>,&amp;buf[i]);</span><br><span class="line">		sem_post(full);</span><br><span class="line">		<span class="keyword">if</span>(i==<span class="number">9</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			i=<span class="number">-1</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;	</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">worker2</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)</span><br><span class="line">	&#123;</span><br><span class="line">		sem_wait(full);</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">"输出：%c\n"</span>,buf[i]);</span><br><span class="line">		sem_post(empty);</span><br><span class="line">		sleep(<span class="number">1</span>);</span><br><span class="line">		<span class="keyword">if</span>(i==<span class="number">9</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			i=<span class="number">-1</span>;		</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;	</span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	empty=sem_open(<span class="string">"empty_"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">10</span>);</span><br><span class="line">	full=sem_open(<span class="string">"full_"</span>,O_CREAT,<span class="number">0666</span>,<span class="number">0</span>);</span><br><span class="line">	<span class="keyword">pthread_t</span> p1,p2;</span><br><span class="line">	pthread_create(&amp;p1,<span class="literal">NULL</span>,worker1,<span class="literal">NULL</span>);</span><br><span class="line">	pthread_create(&amp;p2,<span class="literal">NULL</span>,worker2,<span class="literal">NULL</span>);</span><br><span class="line">	pthread_join(p1,<span class="literal">NULL</span>);</span><br><span class="line">	pthread_join(p2,<span class="literal">NULL</span>);</span><br><span class="line">	sem_close(empty);</span><br><span class="line">	sem_close(full);</span><br><span class="line">	sem_unlink(<span class="string">"empty_"</span>);</span><br><span class="line">	sem_unlink(<span class="string">"full_"</span>);</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li>work1是输入线程调用的函数，worker2是输出线程调用的函数。</li>
<li>设置两个信号量empty和ful来控制程序的执行，其中empty信号量用于保证输入线程在写入数据到缓存的时候缓存中还有空余的位置，保证写入线程后写入的数据不会把前面写入但是为输出的数据给覆盖掉，其初始值为10，表示最开始缓存中有10个空余的位置供给写入线程写入数据；full信号量是用于保证输出线程有数据输出，避免在写入线程还没有写入数据的情况下输出线程输出随机数据，其初始值为0，表示初始状态下缓存中没有数据可以输出</li>
<li>输入线程在写入一个数据前要等待empty信号量，进入后便消耗一个信号量；完成写入数据操作之后post一个full信号量，通知输出线程输出数据。</li>
<li>输出线程在输出一个数据之前哟啊等待full信号量，进出输出操作后便消耗一个full信号量；完成输出操作后post一个empty信号量，通知写入线程缓存又多一个空余位置以供写入数据。</li>
<li>输出线程每输出一个字符等待一秒钟，方便实验结果的查看。</li>
</ul>
</li>
<li><p>编译源代码</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc task3.c -o task3 -lpthread</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="3-3-实验结果"><a href="#3-3-实验结果" class="headerlink" title="3.3 实验结果"></a>3.3 实验结果</h3><h4 id="3-3-1-实验运行现象"><a href="#3-3-1-实验运行现象" class="headerlink" title="3.3.1 实验运行现象"></a>3.3.1 实验运行现象</h4><ol>
<li><p>随机输入字母和数字（10个以内）：124365abc</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173913.jpg" alt></p>
</li>
<li><p>随机输入字母和数字(10个以上)：123456789abcdefg</p>
</li>
</ol>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173917.jpg" alt></p>
<ol>
<li><p>不间断输入：</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173920.jpg" alt></p>
<p>通过观察上面的实验现象，可以看到已经满足了实验题目的要求。</p>
</li>
</ol>
<h4 id="3-3-2-实验现象解释"><a href="#3-3-2-实验现象解释" class="headerlink" title="3.3.2 实验现象解释"></a>3.3.2 实验现象解释</h4><ul>
<li>在第一种类型的测试中，输入数据不大于10个字符的时候，由于empty的信号量初始值为10，所以输入进程会一直连续不断的向缓存中写入数据，每写入一个数据，便post一个full信号量，输出线程便能按序输出字符。</li>
<li><p>在第二种类型的测试中，输入数据大于10个字符的时候，由于empty的初始值为10，所以输入的字符中开始的时候只有前10个字符被写入缓存中，其他的在I/O缓冲区等待输入，当输出线程接收到输入线程post的信号量的时候便会开始输出，每输出一个字符便会post一个empty信号量，当输入线程接收到empty信号量的时候有开始从I/O缓冲区读取数据写入到缓存中。</p>
</li>
<li><p>第三种测试和第二种类似，在输出的过程中间输入数据，原理其实是一样的。</p>
</li>
</ul>
<h2 id="Task-4"><a href="#Task-4" class="headerlink" title="Task 4"></a>Task 4</h2><h3 id="4-1-实验要求"><a href="#4-1-实验要求" class="headerlink" title="4.1 实验要求"></a>4.1 实验要求</h3><ol>
<li><p>通过实验测试，验证共享内存的代码中，receiver能否正确读出sender发送的字符串？如果把其中互斥的代码删除，观察实验结果有何不同？如果在发送和接收进程中打印输出共享内存地址，他们是否相同，为什么？</p>
</li>
<li><p>有名管道和无名管道通信系统调用是否已经实现了同步机制？通过实验验证，发送者和接收者如何同步的。比如，在什么情况下，发送者会阻塞，什么情况下，接收者会阻塞？</p>
</li>
<li>消息通信系统调用是否已经实现了同步机制？通过实验验证，发送者和接收者如何同步的。比如，在什么情况下，发送者会阻塞，什么情况下，接收者会阻塞？</li>
</ol>
<h3 id="4-2-实验过程"><a href="#4-2-实验过程" class="headerlink" title="4.2 实验过程"></a>4.2 实验过程</h3><blockquote>
<p>实验过程根据实验要求的三个部分，对应的过程也分为三个部分，具体如下所示</p>
</blockquote>
<h4 id="4-2-1-内存共享"><a href="#4-2-1-内存共享" class="headerlink" title="4.2.1 内存共享"></a>4.2.1 内存共享</h4><ol>
<li><p>实验源码</p>
<blockquote>
<p>内存内存共享实验的源码分为两个部分，分别是Sender.c和Receive.c,</p>
</blockquote>
<p><code>Sender.c</code>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Filename: Sender.c</span></span><br><span class="line"><span class="comment"> * Description: </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/sem.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/shm.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">key_t</span>  key;</span><br><span class="line">    <span class="keyword">int</span> shm_id;</span><br><span class="line">    <span class="keyword">int</span> sem_id;</span><br><span class="line">    <span class="keyword">int</span> value = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//1.Product the key</span></span><br><span class="line">    key = ftok(<span class="string">"."</span>, <span class="number">0xFF</span>);</span><br><span class="line">    <span class="comment">//2. Creat semaphore for visit the shared memory</span></span><br><span class="line">    sem_id = semget(key, <span class="number">1</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == sem_id)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//3. init the semaphore, sem=0</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == (semctl(sem_id, <span class="number">0</span>, SETVAL, value)))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semctl"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//4. Creat the shared memory(1K bytes)</span></span><br><span class="line">    shm_id = shmget(key, <span class="number">1024</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == shm_id)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//5. attach the shm_id to this process</span></span><br><span class="line">    <span class="keyword">char</span> *shm_ptr;</span><br><span class="line">    shm_ptr = shmat(shm_id, <span class="literal">NULL</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="literal">NULL</span> == shm_ptr)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmat"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//6. Operation procedure</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sem_b</span>;</span></span><br><span class="line">    sem_b.sem_num = <span class="number">0</span>;      <span class="comment">//first sem(index=0)</span></span><br><span class="line">    sem_b.sem_flg = SEM_UNDO;</span><br><span class="line">    sem_b.sem_op = <span class="number">1</span>;           <span class="comment">//Increase 1,make sem=1</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="number">0</span> == (value = semctl(sem_id, <span class="number">0</span>, GETVAL)))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\nNow, snd message process running:\n"</span>);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\tInput the snd message:  "</span>);</span><br><span class="line">            <span class="built_in">scanf</span>(<span class="string">"%s"</span>, shm_ptr);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(<span class="number">-1</span> == semop(sem_id, &amp;sem_b, <span class="number">1</span>))</span><br><span class="line">            &#123;</span><br><span class="line">                perror(<span class="string">"semop"</span>);</span><br><span class="line">                <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//if enter "end", then end the process</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">0</span> == (<span class="built_in">strcmp</span>(shm_ptr ,<span class="string">"end"</span>)))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\nExit sender process now!\n"</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    shmdt(shm_ptr);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Receiver.c</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/sem.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/shm.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">key_t</span>  key;</span><br><span class="line">    <span class="keyword">int</span> shm_id;</span><br><span class="line">    <span class="keyword">int</span> sem_id;</span><br><span class="line">    <span class="keyword">int</span> value = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//1.Product the key</span></span><br><span class="line">    key = ftok(<span class="string">"."</span>, <span class="number">0xFF</span>);</span><br><span class="line">    <span class="comment">//2. Creat semaphore for visit the shared memory</span></span><br><span class="line">    sem_id = semget(key, <span class="number">1</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == sem_id)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//3. init the semaphore, sem=0</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == (semctl(sem_id, <span class="number">0</span>, SETVAL, value)))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semctl"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//4. Creat the shared memory(1K bytes)</span></span><br><span class="line">    shm_id = shmget(key, <span class="number">1024</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == shm_id)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//5. attach the shm_id to this process</span></span><br><span class="line">    <span class="keyword">char</span> *shm_ptr;</span><br><span class="line">    shm_ptr = shmat(shm_id, <span class="literal">NULL</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="literal">NULL</span> == shm_ptr)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmat"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//6. Operation procedure</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sem_b</span>;</span></span><br><span class="line">    sem_b.sem_num = <span class="number">0</span>;      <span class="comment">//first sem(index=0)</span></span><br><span class="line">    sem_b.sem_flg = SEM_UNDO;</span><br><span class="line">    sem_b.sem_op = <span class="number">-1</span>;           <span class="comment">//Increase 1,make sem=1</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="number">1</span> == (value = semctl(sem_id, <span class="number">0</span>, GETVAL)))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\nNow, receive message process running:\n"</span>);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\tThe message is : %s\n"</span>, shm_ptr);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(<span class="number">-1</span> == semop(sem_id, &amp;sem_b, <span class="number">1</span>))</span><br><span class="line">            &#123;</span><br><span class="line">                perror(<span class="string">"semop"</span>);</span><br><span class="line">                <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//if enter "end", then end the process</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">0</span> == (<span class="built_in">strcmp</span>(shm_ptr ,<span class="string">"end"</span>)))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\nExit the receiver process now!\n"</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    shmdt(shm_ptr);</span><br><span class="line">    <span class="comment">//7. delete the shared memory</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == shmctl(shm_id, IPC_RMID, <span class="literal">NULL</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmctl"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//8. delete the semaphore</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == semctl(sem_id, <span class="number">0</span>, IPC_RMID))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semctl"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<blockquote>
<p>下面以sender.c为例解释一下如何创建共享内存并通过信号量机制实现互斥访问从而达到进程间通信的目的。</p>
</blockquote>
<ul>
<li><p>创建一个共享内存的ID,就是代码中的key</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">key_t</span>  key;</span><br><span class="line">key = ftok(<span class="string">"."</span>, <span class="number">0xFF</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p> 通过ftok函数创建一个key_t类型的变量，作为共享内存的key，ftok函数的两个参数分别是文档名(一个存在的路径),上例中的路径是<code>.</code>表示当前路径，另一个参数是子序号</p>
</blockquote>
</li>
<li><p>创建并初始化信号量</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> sem_id;</span><br><span class="line">sem_id = semget(key, <span class="number">1</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="number">-1</span> == sem_id)</span><br><span class="line">&#123;</span><br><span class="line">    perror(<span class="string">"semget"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(<span class="number">-1</span> == (semctl(sem_id, <span class="number">0</span>, SETVAL, value)))</span><br><span class="line">&#123;</span><br><span class="line">    perror(<span class="string">"semctl"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>通过semget()函数创建一个信号量，初始值为1，再通过semctl()函数初始化该信号量</p>
</blockquote>
</li>
<li><p>创建共享内存并挂载在进程中</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//4. Creat the shared memory(1K bytes)</span></span><br><span class="line">shm_id = shmget(key, <span class="number">1024</span>, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="number">-1</span> == shm_id)</span><br><span class="line">&#123;</span><br><span class="line">    perror(<span class="string">"shmget"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//5. attach the shm_id to this process</span></span><br><span class="line"><span class="keyword">char</span> *shm_ptr;</span><br><span class="line">shm_ptr = shmat(shm_id, <span class="literal">NULL</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="literal">NULL</span> == shm_ptr)</span><br><span class="line">&#123;</span><br><span class="line">    perror(<span class="string">"shmat"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在这部分代码中，首先通过shmget()函数创建了一个大小为1000B的共享内存，然后通过shmat函数，将刚刚创建的共享内存以可读写的方式挂载在进程上，并且指定系统将自动选择一个合适的地址给共享内存，将挂载的共享内存地址赋值给char型指针shm_ptr</p>
</blockquote>
</li>
<li><p>Sender主循环</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == (value = semctl(sem_id, <span class="number">0</span>, GETVAL)))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\nNow, snd message process running:\n"</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\tInput the snd message:  "</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">"%s"</span>, shm_ptr);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">-1</span> == semop(sem_id, &amp;sem_b, <span class="number">1</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            perror(<span class="string">"semop"</span>);</span><br><span class="line">            <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//if enter "end", then end the process</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == (<span class="built_in">strcmp</span>(shm_ptr ,<span class="string">"end"</span>)))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\nExit sender process now!\n"</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>主循环中首先判断表示共享内存访问情况的信号量是否为0(为0表示共享内存空闲)，如果为0的话提示用户输入想要输入的消息，并将用户输入的消息写入共享内存中，写完后通过semop函数将信号量加一，通知receiver读取消息。并且定义一个<code>end</code>命令表示退出当前进程。循环退出的时候取消共享内存的挂载</p>
</blockquote>
</li>
<li><p>Receiver主循环</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">1</span> == (value = semctl(sem_id, <span class="number">0</span>, GETVAL)))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\nNow, receive message process running:\n"</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\tThe message is : %s\n"</span>, shm_ptr);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">-1</span> == semop(sem_id, &amp;sem_b, <span class="number">1</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            perror(<span class="string">"semop"</span>);</span><br><span class="line">            <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//if enter "end", then end the process</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == (<span class="built_in">strcmp</span>(shm_ptr ,<span class="string">"end"</span>)))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\nExit the receiver process now!\n"</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>主循环中首先判断表示共享内存访问情况的信号量是否为1(为1表示共享内存已经写入消息，可以读取)，如果为1的话输出该消息，输出后通过semop函数将信号量减1，通知Sender可以再次写入消息。并且定义一个<code>end</code>命令表示退出当前进程。循环退出的时候取消共享内存的挂载</p>
</blockquote>
</li>
</ul>
</li>
<li><p>实验现象</p>
<p>将上述源码编译后进行测试，得到下面的结果。</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173932.jpg" alt></p>
<blockquote>
<p>可以看到sender进程发出的消息receiver进程均准确无误的收到</p>
</blockquote>
</li>
<li><p>删除互斥访问相关的代码</p>
<p>程序主要的代码没有变化，只是在Sender和Receiver进程的主循环中将用于控制互斥访问共享内存的相关代码删除，注释后的结果如下：</p>
<p><code>Sender_2.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"\nNow, snd message process running:\n"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"\tInput the snd message:  "</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">"%s"</span>, shm_ptr);</span><br><span class="line">    <span class="comment">//if enter "end", then end the process</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == (<span class="built_in">strcmp</span>(shm_ptr ,<span class="string">"end"</span>)))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"\nExit sender process now!\n"</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Receiver_2.c:</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">while(1)</span><br><span class="line">&#123;</span><br><span class="line">    printf(&quot;\nNow, receive message process running:\n&quot;);</span><br><span class="line">    printf(&quot;\tThe message is : %s\n&quot;, shm_ptr);</span><br><span class="line"></span><br><span class="line">    //if enter &quot;end&quot;, then end the process</span><br><span class="line">    if(0 == (strcmp(shm_ptr ,&quot;end&quot;)))</span><br><span class="line">    &#123;</span><br><span class="line">        printf(&quot;\nExit the receiver process now!\n&quot;);</span><br><span class="line">        break;</span><br><span class="line">    &#125;</span><br><span class="line">    sleep(3);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>最后加一个sleep(1)用于控制打印的速度，便于观察现象</p>
</blockquote>
</li>
<li><p>删除互斥访问后的实验现象</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173937.jpg" alt></p>
<blockquote>
<p>实验现象解释：当删除互斥访问之后，两个进程便没有限制的访问共享内存，Sender进程由于受限于用户输入的速度，会停留一直等待用户输入数据，但是Receiver进程会一直输出共享内存中的消息。</p>
</blockquote>
</li>
<li><p>打印Sender和Receiver进程中共享内存的地址</p>
<p>在原始代码的基础上修改，具体代码文件分别是<code>Sender_3.c</code>和<code>Receiver_3.c</code>，具体修改就是如下：</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173942.jpg" alt></p>
<p>在挂载共享内存后打印挂载后的地址</p>
</li>
<li><p>打印共享内存地址实验现象</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173945.jpg" alt></p>
<blockquote>
<p>可以看到实验现象，在两个进程中共享内存的地址不一样</p>
</blockquote>
<p>==<strong>现象解释：</strong>==</p>
<p>通过上面的现象可以看到共享内存在不同进程中是不相同的，总结有以下的原因导致共享内存在不同进程中的地址不一样：</p>
<ul>
<li>进程在挂载内存的时候使用的<code>shmat()</code>函数中的第二个参数使用的是NULL，NULL参数的含义是进程让系统分配给共享内存合适的地址。在<code>shmat()</code>函数中，第二个参数有三种选择，分别是：</li>
</ul>
<p>| 参数值 |             NULL             |                             addr                             |                             addr                             |<br>| :——: | :—————————————: | :—————————————————————————————: | :—————————————————————————————: |<br>|  含义  | 系统将自动选择一个合适的地址 | 如果shmaddr非0 并且指定了SHM_RND 则此段连接到shmaddr -（shmaddr mod SHMLAB)所表示的地址上。 | 第三个参数如果在flag中指定了SHM_RDONLY位，则以只读方式连接此段，否则以读写的方式连接此 段。 |</p>
<p>​    可以看到，当addr有具体的值的时候，便将共享内存挂载到指定的地址上</p>
<ul>
<li>现代操作系统中都存在ASLR(地址空间随机化)，ASLR是⼀种针对缓冲区溢出的安全保护机制，具有ASLR机制的操作系统每次加载到内存的程序起始地址会随机变化。系统的这个随机化操作可能导致共享内存的地址不一致。</li>
</ul>
<p>==<strong>验证：</strong>==</p>
<ol>
<li><p>指定Sender_4.c和Receiver_4.c中共享内存的挂载地址为<code>0x7fcc2c0bb000</code></p>
<ul>
<li><p>修改具体的代码如下：</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173951.jpg" alt></p>
</li>
<li><p>运行结果：</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-173955.jpg" alt></p>
<blockquote>
<p>实验结果结果佐证了上面的现象解释，通过指定挂载共享内存的地址，可以使共享内存的地址一致，可以随意指定改地址</p>
</blockquote>
</li>
</ul>
</li>
<li><p>关闭系统的ASLR操作</p>
</li>
</ol>
<ul>
<li>具体的关闭命令如下：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">   sudo su</span><br><span class="line">sysctl -w kernel.randomize_va_space=0</span><br></pre></td></tr></table></figure>
</li>
</ol>
<pre><code>  + 运行结果：

    &lt;div align=&quot;center&quot;&gt;&lt;img src=&quot;&quot; width=&quot;800&quot; /&gt;&lt;/div&gt;

    ![](http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174000.jpg)

    &gt; 这个实验现象也佐证了系统的ASLR也对导致挂载的共享内存地址不一样
</code></pre><h4 id="4-2-2-管道通信"><a href="#4-2-2-管道通信" class="headerlink" title="4.2.2 管道通信"></a>4.2.2 管道通信</h4><h5 id="（1）无名管道"><a href="#（1）无名管道" class="headerlink" title="（1）无名管道"></a>（1）无名管道</h5><ol>
<li><p>实验源码</p>
<p><code>pipe.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;     //for pipe()</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;     //for memset()</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;     //for exit()</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> fd[<span class="number">2</span>];</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">20</span>];</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == pipe(fd))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"pipe"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    write(fd[<span class="number">1</span>], <span class="string">"hello,world"</span>, <span class="number">12</span>);</span><br><span class="line">    <span class="built_in">memset</span>(buf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">    read(fd[<span class="number">0</span>], buf, <span class="number">12</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"The message is: %s\n"</span>, buf);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li>通过pipe函数创建管道，函数传递一个整形数组fd，fd的两个整形数表示的是两个文件描述符，其中第一个用于读取数据，第二个用于写数据。两个描述符相当远管道的两端，一段负责写数据，一段负责读数据。</li>
<li>pipe管道是半双工的工作模式，某一时刻只能读或者只能写</li>
<li>读写管道就和读写普通文件一样，使用write和read</li>
</ul>
</li>
<li><p>实验现象</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174009.jpg" alt></p>
</li>
<li><p>无名管道同步机制验证</p>
<blockquote>
<p>为了验证无名管道的同步机制，在上述代码的基础上进行修改，得到如下的代码</p>
</blockquote>
<p><code>pipe_2.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;     //for pipe()</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;     //for memset()</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;     //for exit()</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> fd[<span class="number">2</span>];</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">200</span>]=&#123;<span class="number">0</span>&#125;;</span><br><span class="line">	<span class="keyword">pid_t</span> child;</span><br><span class="line">    <span class="comment">//创建管道</span></span><br><span class="line">	<span class="keyword">if</span>(<span class="number">-1</span> == pipe(fd))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"pipe"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//创建子进程</span></span><br><span class="line">	child=fork();</span><br><span class="line">	<span class="keyword">if</span>(child==<span class="number">-1</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		perror(<span class="string">"fork"</span>);</span><br><span class="line">		<span class="built_in">exit</span>(<span class="string">"EXIT_FAILURE"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span>(child==<span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="comment">//关闭子进程中不需要的写描述符</span></span><br><span class="line">		close(fd[<span class="number">1</span>]);</span><br><span class="line">		<span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">if</span>(read(fd[<span class="number">0</span>],buf,<span class="keyword">sizeof</span>(buf))&gt;<span class="number">0</span>)</span><br><span class="line">					<span class="built_in">printf</span>(<span class="string">"子进程接收的消息是:%s\n"</span>,buf);</span><br><span class="line">			<span class="keyword">else</span></span><br><span class="line">					<span class="built_in">printf</span>(<span class="string">"子进程:管道中没有数据\n"</span>);</span><br><span class="line">			sleep(<span class="number">2</span>);		</span><br><span class="line">			<span class="keyword">if</span>(<span class="built_in">strcmp</span>(buf,<span class="string">"end"</span>)==<span class="number">0</span>)</span><br><span class="line">					<span class="keyword">break</span>;</span><br><span class="line">			<span class="built_in">memset</span>(buf,<span class="number">0</span>,<span class="keyword">sizeof</span>(buf));</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span>(child&gt;<span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		close(fd[<span class="number">0</span>]);</span><br><span class="line">		<span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">"父进程中-请输入消息:"</span>);</span><br><span class="line">			<span class="built_in">scanf</span>(<span class="string">"%s"</span>,buf);</span><br><span class="line">			write(fd[<span class="number">1</span>],buf,<span class="built_in">strlen</span>(buf));</span><br><span class="line">			<span class="keyword">if</span>(<span class="built_in">strcmp</span>(buf,<span class="string">"end"</span>)==<span class="number">0</span>)</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">		&#125;	</span><br><span class="line">	&#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>对于上述代码做出如下解释：父进程是消息的发送者，在父进程中创建了两个文件描述符，fork一个子进程的时候会复制这两个管道文件描述符，因此父进程和子进程都会将自己的那个用不到的文件描述符关闭。父进程中会持续向管道中写入用户输入的消息，子进程会一直输出管道中的消息，如果管道中没有消息就会阻塞等待。</p>
</blockquote>
</li>
<li><p>无名管道同步机制实验现象</p>
<div align="center"><img src width="600"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174012.jpg" alt></p>
<blockquote>
<p>可以看到输出进程是按照输入进程输入的顺序输出数据，并且当输入进程没有数据输入，即管道中没有数据的时候，输出进程会阻塞。因此无名管道通信系统调用的时候已经yijing实现了同步机制</p>
</blockquote>
</li>
<li><p>无名管道同步机制原理</p>
<p>通过上面的实验和查阅相关资料，得到无名管道如下的同步机制：</p>
<ul>
<li><p>管道的读写通过两个系统调用write和read实现</p>
</li>
<li><p>发送者在向管道内存中写入数据之前，首先<strong>检查内存是否被读进程锁定</strong>和<strong>内存中是否还有剩余空间</strong>，如果这两个要求都满足的话write函数会对内存上锁，然后进行写入数据，写完之后解锁；否则就会等待(阻塞)。</p>
</li>
<li>写进程在读取管道中的数据之前，也会<strong>检查内存是否被读进程锁定</strong>和<strong>管道内存中是否有数据</strong>，如果满足这两个条件，read函数会对内存上锁，读取数据后在解锁；否则会等到(阻塞)</li>
</ul>
</li>
</ol>
<h5 id="（2）有名管道"><a href="#（2）有名管道" class="headerlink" title="（2）有名管道"></a>（2）有名管道</h5><ol>
<li><p>实验代码</p>
<blockquote>
<p>有名管道实验中设计两个代码文件<code>fifo_send.c</code>和<code>fifo_rcv.c</code></p>
</blockquote>
<p><code>fifo_send.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FIFO <span class="meta-string">"./my_fifo"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> buf[] = <span class="string">"hello,world"</span>;</span><br><span class="line">    <span class="comment">//1. check the fifo file existed or not</span></span><br><span class="line">    <span class="keyword">int</span> ret;</span><br><span class="line">    ret = access(FIFO, F_OK);</span><br><span class="line">    <span class="keyword">if</span>(ret != <span class="number">0</span>)    <span class="comment">//file /tmp/my_fifo existed</span></span><br><span class="line">    &#123;</span><br><span class="line">    	<span class="keyword">if</span>(<span class="number">-1</span> == mkfifo(FIFO, <span class="number">0766</span>))</span><br><span class="line">    	&#123;</span><br><span class="line">    	    perror(<span class="string">"mkfifo"</span>);</span><br><span class="line">    	    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    	&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//3.Open the fifo file</span></span><br><span class="line">    <span class="keyword">int</span> fifo_fd;</span><br><span class="line">    fifo_fd = open(FIFO, O_WRONLY);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == fifo_fd)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"open"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//4. write the fifo file</span></span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">    num = write(fifo_fd, buf, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">    <span class="keyword">if</span>(num &lt; <span class="keyword">sizeof</span>(buf))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"write"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"write the message ok!\n"</span>);</span><br><span class="line"></span><br><span class="line">    close(fifo_fd);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>fifo_rcv.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> *File: fifo_rcv.c</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FIFO <span class="meta-string">"./my_fifo"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">20</span>] ;</span><br><span class="line">    <span class="built_in">memset</span>(buf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line"></span><br><span class="line">    <span class="comment">//`. check the fifo file existed or not</span></span><br><span class="line">    <span class="keyword">int</span> ret;</span><br><span class="line">    ret = access(FIFO, F_OK);</span><br><span class="line">    <span class="keyword">if</span>(ret != <span class="number">0</span>)    <span class="comment">//file /tmp/my_fifo existed</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="number">-1</span>==mkfifo(FIFO,<span class="number">0766</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            perror(<span class="string">"mkfifo"</span>); </span><br><span class="line">            <span class="built_in">exit</span>(<span class="string">"EXIT_FAILURE"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//	2.Open the fifo file</span></span><br><span class="line">    <span class="keyword">int</span> fifo_fd;</span><br><span class="line">    fifo_fd = open(FIFO, O_RDONLY);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == fifo_fd)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"open"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//4. read the fifo file</span></span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">    num = read(fifo_fd, buf, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Read %d words: %s\n"</span>, num, buf);</span><br><span class="line">    close(fifo_fd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li>写进程fifo_send分为四个步骤执行，首先判断当前目录下是否已经存在my_fifo文件，不存在的话在当前目录下通过mkfifo()函数创建FIFO类型的文件my_fifo；再通过open()函数打开my_fifo文件，最后向文件中写入消息；</li>
<li>读进程的过程和写进程的类似，只没有了创建fifo文件的过程而已</li>
</ul>
</li>
<li><p>实验现象</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174019.jpg" alt></p>
<blockquote>
<p>现象描述：在仅仅只运行fifo_send进程的时候，没有任何输出，进程一直阻塞，直到fifo_rcv进程运行，两个进程才开始输出信息。</p>
</blockquote>
<p>当写进程和读进程都设置成阻塞状态的时候，不论先执行那个进程，先执行的进程都会阻塞等待，待另一个进程执行后两个进程才正常执行。</p>
</li>
<li><p>探究有名管道的同步和阻塞机制</p>
<p>通过<code>fifo_fd=open(FIFO,O_RDONLY | O_NONBLOCK)</code>设置为非阻塞状态，<code>fifo_fd=open(FIFO,O_RDONLY)</code>设置为阻塞状态，对应四个进程分别为fifo_send(阻塞)、fifo_rcv(阻塞)、fifo_send_1(非阻塞)、fifo_rcv_1(非阻塞)</p>
<ul>
<li><p>读进程阻塞、写进程阻塞</p>
<ul>
<li><p>先执行fifo_send后执行fifo_rcv，结果正确</p>
<p>截图请见上面的实验现象</p>
</li>
<li><p>先执行fifo_rcv后执行fifo_send，结果正确</p>
</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174023.jpg" alt></p>
<p>具体的原因是读进程在open FIFO的时候由于没有s</p>
<p>通过查阅资料得到了FIFO管道的阻塞机制如下：</p>
<p>对于设置了阻塞的读进程而言：</p>
<blockquote>
<ol>
<li>读进程阻塞的原因有三种：FIFO 中没有数据、有其他的读进程正在读取这些数据、没有写进程打开FIFO文件</li>
<li>不论是哪种原因引起的阻塞，解开阻塞的原因都是FIFO有新的数据写入</li>
<li>如果一个读进程有多个read操作，那么只会阻塞第一个read，其他的不会发生阻塞</li>
</ol>
</blockquote>
<p>对于设置了阻塞的写进程而言：</p>
</li>
</ul>
<blockquote>
<ol>
<li>当写入的数据量小于PIPE_BUF时，Linux保证写入原子性。如果此时管道中的空闲位置不足以容纳要写入的数据，泽写进程阻塞，直到管道中空间足够，一次性写入所有数据</li>
<li>当写入的数据量大于PIPE_BUF时，Linux不再保证写入的原子性。一旦管道中有空闲位置便尝试写入数据，直到所有数据写入完成后返回。</li>
</ol>
</blockquote>
<ul>
<li><p>读进程阻塞，写进程非阻塞</p>
<ul>
<li>先执行fifo_send_1后执行fifo_rcv，写进程open函数返回-1</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174026.jpg" alt></p>
<ul>
<li>先执行fifo_rcv后执行fifo_send_1，结果正常</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174030.jpg" alt></p>
</li>
<li><p>读进程非阻塞，写进程阻塞</p>
<ul>
<li>先执行fifo_send后执行fifo_rcv_1,结果正常</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174035.jpg" alt></p>
<ul>
<li>先执行fifo_rcv_1后执行fifo_send，程序崩溃</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174039.jpg" alt></p>
</li>
<li><p>读写进程都是非阻塞</p>
<ul>
<li>先执行fifo_send_1后执行fifo_rcv_1</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174043.jpg" alt></p>
<ul>
<li>先执行fifo_rcv_1后执行fifo_send_1</li>
</ul>
<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174046.jpg" alt></p>
</li>
</ul>
</li>
</ol>
<h4 id="4-2-3-消息队列"><a href="#4-2-3-消息队列" class="headerlink" title="4.2.3 消息队列"></a>4.2.3 消息队列</h4><ol>
<li><p>实验代码</p>
<blockquote>
<p>本实验代码文件分为Server.c和Client.c两个</p>
</blockquote>
<p><code>Server.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/msg.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BUF_SIZE 128</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//Rebuild the strcut (must be)</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext[BUF_SIZE];</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//1. creat a mseg queue</span></span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="keyword">int</span> msgId;</span><br><span class="line">    </span><br><span class="line">    key = ftok(<span class="string">"."</span>, <span class="number">0xFF</span>);</span><br><span class="line">    msgId = msgget(key, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == msgId)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Process (%s) is started, pid=%d\n"</span>, argv[<span class="number">0</span>], getpid());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        alarm(<span class="number">0</span>);</span><br><span class="line">        alarm(<span class="number">600</span>);     <span class="comment">//if doesn't receive messge in 600s, timeout &amp; exit</span></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span> <span class="title">rcvBuf</span>;</span></span><br><span class="line">        <span class="built_in">memset</span>(&amp;rcvBuf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(struct msgbuf));</span><br><span class="line">        msgrcv(msgId, &amp;rcvBuf, BUF_SIZE, <span class="number">1</span>, <span class="number">0</span>);                </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Receive msg: %s\n"</span>, rcvBuf.mtext);</span><br><span class="line">        </span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span> <span class="title">sndBuf</span>;</span></span><br><span class="line">        <span class="built_in">memset</span>(&amp;sndBuf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(sndBuf));</span><br><span class="line"></span><br><span class="line">        <span class="built_in">strncpy</span>((sndBuf.mtext), (rcvBuf.mtext), <span class="built_in">strlen</span>(rcvBuf.mtext)+<span class="number">1</span>);</span><br><span class="line">        sndBuf.mtype = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">-1</span> == msgsnd(msgId, &amp;sndBuf, <span class="built_in">strlen</span>(rcvBuf.mtext)+<span class="number">1</span>, <span class="number">0</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            perror(<span class="string">"msgsnd"</span>);</span><br><span class="line">            <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">        &#125;</span><br><span class="line">            </span><br><span class="line">        <span class="comment">//if scanf "end~", exit</span></span><br><span class="line">        <span class="keyword">if</span>(!<span class="built_in">strcmp</span>(<span class="string">"end~"</span>, rcvBuf.mtext))</span><br><span class="line">             <span class="keyword">break</span>;</span><br><span class="line">    &#125;     </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"THe process(%s),pid=%d exit~\n"</span>, argv[<span class="number">0</span>], getpid());</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Client.c:</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/msg.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BUF_SIZE 128</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//Rebuild the strcut (must be)</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext[BUF_SIZE];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//1. creat a mseg queue</span></span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="keyword">int</span> msgId;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"THe process(%s),pid=%d started~\n"</span>, argv[<span class="number">0</span>], getpid());</span><br><span class="line"></span><br><span class="line">    key = ftok(<span class="string">"."</span>, <span class="number">0xFF</span>);</span><br><span class="line">    msgId = msgget(key, IPC_CREAT|<span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == msgId)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2. creat a sub process, wait the server message</span></span><br><span class="line">    <span class="keyword">pid_t</span> pid;</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">-1</span> == (pid = fork()))</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"vfork"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//In child process</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == pid)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            alarm(<span class="number">0</span>);</span><br><span class="line">            alarm(<span class="number">100</span>);     <span class="comment">//if doesn't receive messge in 100s, timeout &amp; exit</span></span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span> <span class="title">rcvBuf</span>;</span></span><br><span class="line">            <span class="built_in">memset</span>(&amp;rcvBuf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(struct msgbuf));</span><br><span class="line">            msgrcv(msgId, &amp;rcvBuf, BUF_SIZE, <span class="number">2</span>, <span class="number">0</span>);                </span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"Server said: %s\n"</span>, rcvBuf.mtext);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">else</span>    <span class="comment">//parent process</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            usleep(<span class="number">100</span>);</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">msgbuf</span> <span class="title">sndBuf</span>;</span></span><br><span class="line">            <span class="built_in">memset</span>(&amp;sndBuf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(sndBuf));</span><br><span class="line">            <span class="keyword">char</span> buf[BUF_SIZE] ;</span><br><span class="line">            <span class="built_in">memset</span>(buf, <span class="string">'\0'</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">            </span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"\nInput snd mesg: "</span>);</span><br><span class="line">            <span class="built_in">scanf</span>(<span class="string">"%s"</span>, buf);</span><br><span class="line">            </span><br><span class="line">            <span class="built_in">strncpy</span>(sndBuf.mtext, buf, <span class="built_in">strlen</span>(buf)+<span class="number">1</span>);</span><br><span class="line">            sndBuf.mtype = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(<span class="number">-1</span> == msgsnd(msgId, &amp;sndBuf, <span class="built_in">strlen</span>(buf)+<span class="number">1</span>, <span class="number">0</span>))</span><br><span class="line">            &#123;</span><br><span class="line">                perror(<span class="string">"msgsnd"</span>);</span><br><span class="line">                <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">            &#125;            </span><br><span class="line">            <span class="comment">//if scanf "end~", exit</span></span><br><span class="line">            <span class="keyword">if</span>(!<span class="built_in">strcmp</span>(<span class="string">"end~"</span>, buf))</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"THe process(%s),pid=%d exit~\n"</span>, argv[<span class="number">0</span>], getpid());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>程序解释</p>
<ul>
<li><p>程序分为服务器端和客户端，客户端向服务器发起通信，服务器端收到数据后将一模一样的数据返回</p>
</li>
<li><p>通过mgsrcv函数读取客户端传过来的消息，msgrcv的参数列表见下面。</p>
<p><code>int msgrcv(int msqid, void  *ptr, size_t  length, long  type, int  flag);</code></p>
</li>
</ul>
<p>| 参数 |     msgid      |      ptr       |    length    |                             type                             |                          flag                          |<br>| :—: | :——————: | :——————: | :—————: | :—————————————————————————————: | :——————————————————————————: |<br>| 含义 | 消息队列标识符 | 消息缓冲区指针 | 消息数据长度 |                 决定从队列中返回那一条下消息                 |                        阻塞与否                        |<br>| 备注 |                |                |              | =0 返回消息队列中第一条消息<br>&gt;0 返回消息队列中等于mtype 类型的第一条消息。<br>&lt;0 返回mtype&lt;=type 绝对值最小值的第一条消息。 | msgflg 为０表示阻塞方式，设置IPC_NOWAIT 表示非阻塞方式 |</p>
<ul>
<li>通过msgsnd函数向消息队列中加入消息，msgsnd的参数列表见下面。</li>
</ul>
<p><code>int msgsnd(int  msqid, const  void   *ptr, size_t    length, int   flag);</code></p>
<p>| 参数 |     msgid      |      ptr       |    length    |                          flag                          |<br>| :—: | :——————: | :——————: | :—————: | :——————————————————————————: |<br>| 含义 | 消息队列标识符 | 消息缓冲区指针 | 消息数据长度 |                        阻塞与否                        |<br>| 备注 |                |                |              | msgflg 为０表示阻塞方式，设置IPC_NOWAIT 表示非阻塞方式 |</p>
<ul>
<li>客户端的子进程主要负责消息的接受，父进程主要负责消息的发送；</li>
<li>通过分析上面的代码可以知道，客户端和服务器端都是以阻塞的方式读取和写入消息</li>
</ul>
</li>
<li><p>程序运行结果</p>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174053.jpg" alt></p>
</li>
<li><p>探究消息队列的同步和阻塞机制</p>
<blockquote>
<p>通过上面的程序解释中可以看出，消息队列通过msgrcv和msgsnd两个函数的flag参数控制是否阻塞，将其设置为IPC_NOWAIT表示不阻塞；如果客户端和服务器端都设置阻塞话，就可以达到同步的目的</p>
</blockquote>
<p>现在做出如下探究：</p>
<ul>
<li>客服端不阻塞(代码为Client_1.c),服务器端阻塞，得到结果如下。</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174058.jpg" alt></p>
<blockquote>
<p>可以看到当客户端不阻塞的话在客户端接受服务器端消息的时候会无限制的打印消息队列中的空消息，哪怕消息队列中没有任何消息</p>
</blockquote>
<ul>
<li>客户端阻塞，服务器端不阻塞(代码为Server_1.c)</li>
</ul>
<div align="center"><img src width="800"></div>

<p><img src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-04-29-174105.jpg" alt></p>
<blockquote>
<p>可以看到当服务器端没有设置阻塞的时候，服务器端会一直接受消息队列中的空消息并向客户端转发。 </p>
</blockquote>
</li>
</ol>
<h2 id="Task-5"><a href="#Task-5" class="headerlink" title="Task 5"></a>Task 5</h2><blockquote>
<p>本实验分析进程上下文切换的代码，说明实现的保存和恢复的上下文内容以及进程切换的工作流程。</p>
</blockquote>
<p>我们首先从<code>devices/timer.c</code>文件中的timer_sleep函数开始 分析，下面是该函数的具体代码：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Sleeps for approximately TICKS timer ticks.  Interrupts must</span></span><br><span class="line"><span class="comment">   be turned on. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">timer_sleep</span> <span class="params">(<span class="keyword">int64_t</span> ticks)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int64_t</span> start = timer_ticks ();</span><br><span class="line">  ASSERT (intr_get_level () == INTR_ON);</span><br><span class="line">  <span class="keyword">while</span> (timer_elapsed (start) &lt; ticks) </span><br><span class="line">    thread_yield ();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>下面开始逐行分析这个函数，第5行的<code>timer_ticks</code>函数也在timer.c文件中，跳转到该函数中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Returns the number of timer ticks since the OS booted. */</span></span><br><span class="line"><span class="keyword">int64_t</span> timer_ticks (<span class="keyword">void</span>) </span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">enum</span> intr_level old_level = intr_disable ();</span><br><span class="line">  <span class="keyword">int64_t</span> t = ticks;</span><br><span class="line">  intr_set_level (old_level);</span><br><span class="line">  <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>timer_ticks</code>函数中第4行涉及一个名为intr_disable()的函数，该函数的具体定义在<code>devices/interrupt.c</code>文件中。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Disables interrupts and returns the previous interrupt status. */</span></span><br><span class="line"><span class="function"><span class="keyword">enum</span> intr_level <span class="title">intr_disable</span> <span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">enum</span> intr_level old_level = intr_get_level ();</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Disable interrupts by clearing the interrupt flag.</span></span><br><span class="line"><span class="comment">     See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable</span></span><br><span class="line"><span class="comment">     Hardware Interrupts". */</span></span><br><span class="line">  <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"cli"</span> : : : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">  <span class="keyword">return</span> old_level;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在看看返回值<code>intr_level</code>是个什么结构,代码在<code>devices/interrupt.h</code>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Interrupts on or off? */</span></span><br><span class="line"><span class="keyword">enum</span> intr_level </span><br><span class="line">  &#123;</span><br><span class="line">    INTR_OFF,             <span class="comment">/* Interrupts disabled. */</span></span><br><span class="line">    INTR_ON               <span class="comment">/* Interrupts enabled. */</span></span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure>
<p>可以发现，intr_level这个枚举类型表示的是是否允许中断。于是分析得到<code>intr_disable</code>函数做了两件事。1. 调用<code>intr_old_level</code>函数 2. 直接执行汇编代码保证这个线程不能被中断。之后返回调用<code>intr_old_level</code>函数的返回值。</p>
<p>再看看<code>intr_get_level</code>函数的实现细节，该函数的定义也在<code>devices/interrupt.c</code>文件中</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Returns the current interrupt status. */</span></span><br><span class="line"><span class="function"><span class="keyword">enum</span> intr_level <span class="title">intr_get_level</span> <span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">uint32_t</span> flags;</span><br><span class="line">  <span class="comment">/* Push the flags register on the processor stack, then pop the</span></span><br><span class="line"><span class="comment">     value off the stack into `flags'.  See [IA32-v2b] "PUSHF"</span></span><br><span class="line"><span class="comment">     and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware</span></span><br><span class="line"><span class="comment">     Interrupts". */</span></span><br><span class="line">  <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"pushfl; popl %0"</span> : <span class="string">"=g"</span> (flags))</span></span>;</span><br><span class="line">  <span class="keyword">return</span> flags &amp; FLAG_IF ? INTR_ON : INTR_OFF;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过注释信息和分析汇编代码可以知道，<code>intr_get_level</code>这个函数的作用是返回当前的中断状态。<code>intr_get_level</code>函数弄清楚了之后，返回上一层函数中，到了<code>intr_disable</code>函数中，这样就可以清楚的知道<code>intr_disable</code>函数的作用：</p>
<ul>
<li>获取当前中断状态</li>
<li>将当前中断状态更改为不可中断</li>
<li>返回先前的中断状态</li>
</ul>
<p>弄清楚了<code>intr_disable</code>函数，接着看<code>timer_ticks</code>函数的5、6、7行</p>
<ul>
<li>第5行通过一个int64_t类型的变量t获取全局变量ticks的值；</li>
<li><p>第6行<code>intr_set_level(old_level)</code>表示将当前中断状态设置为之前的中断状态。</p>
</li>
<li><p>第7行返回t</p>
</li>
</ul>
<p>这样，函数<code>timer_ticks</code>的含义也就弄清楚了。其实<code>timer_ticks</code>函数的作用很简单，就是想获取当前系统的ticks值而已，而上面通过这么大篇幅的介绍<code>timer_ticks</code>函数的4、6两行的作用，原因是第4行和第6行通过先关闭中断，待t获取到ticks值之后载恢复之前的中断状态，来保证操作的原子性，简单的说就是在t获取全局变量ticks的值的时候，不能被打断。</p>
<p>然后接着分析<code>timer_sleep</code>函数的第6行<code>ASSERT (intr_get_level () == INTR_ON);</code>这里是一个断言，当<code>intr_get_lvel</code>函数获取的当前中断状态不是<code>INTR_ON</code>的时候发生警告且退出。</p>
<p><code>timer_sleep</code>函数剩下的就是一个循环了：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (timer_elapsed (start) &lt; ticks) </span><br><span class="line">    thread_yield ();</span><br></pre></td></tr></table></figure>
<p>通过分析不难得出<code>timer_elapsed()</code>函数的作用是计算当前的系统ticks减去之前得到的start的差值，如果这个差值小于函数参数ticks的话一直执行thread_yield()函数。</p>
<p>再看看<code>thread_yield</code>函数的具体定义（在<code>thread/thread.c文件中</code>），分析一下该函数的作用：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Yields the CPU.  The current thread is not put to sleep and</span></span><br><span class="line"><span class="comment">   may be scheduled again immediately at the scheduler's whim. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_yield</span> <span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">cur</span> = <span class="title">thread_current</span> ();</span></span><br><span class="line">  <span class="keyword">enum</span> intr_level old_level;</span><br><span class="line">  ASSERT (!intr_context ());</span><br><span class="line">  old_level = intr_disable ();</span><br><span class="line">  <span class="keyword">if</span> (cur != idle_thread) </span><br><span class="line">    list_push_back (&amp;ready_list, &amp;cur-&gt;elem);</span><br><span class="line">  cur-&gt;status = THREAD_READY;</span><br><span class="line">  schedule ();</span><br><span class="line">  intr_set_level (old_level);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>thread_yield</code>函数第5行顾名思义，作用就是返回当前正在运行的线程，通过一个thread类型的结构体指针接受该函数返回值。</p>
<p><code>thread_yield</code>函数的第7行通过断言的方式判断中断类型，如果是由于I/O等引起的硬中断则退出，如果是软中断的话正常运行。</p>
<p>再看第8行和第13行的之前也分析过，这是保证9-12行操作的原子性。</p>
<p>再分析9-12行：</p>
<ul>
<li>9-10行：如何当前线程不是空闲的线程就调用list_push_back把当前线程的元素扔到就绪队列里面， </li>
<li>11行：把线程改成THREAD_READY状态</li>
<li>12行：调用schedule函数</li>
</ul>
<p>再深入<code>schedule</code>函数(<code>thread/thread.c文件</code>)看看</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Schedules a new process.  At entry, interrupts must be off and</span></span><br><span class="line"><span class="comment">   the running process's state must have been changed from</span></span><br><span class="line"><span class="comment">   running to some other state.  This function finds another</span></span><br><span class="line"><span class="comment">   thread to run and switches to it.</span></span><br><span class="line"><span class="comment">   It's not safe to call printf() until thread_schedule_tail()</span></span><br><span class="line"><span class="comment">   has completed. */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">schedule</span> <span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">cur</span> = <span class="title">running_thread</span> ();</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">next</span> = <span class="title">next_thread_to_run</span> ();</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">prev</span> = <span class="title">NULL</span>;</span></span><br><span class="line"></span><br><span class="line">  ASSERT (intr_get_level () == INTR_OFF);</span><br><span class="line">  ASSERT (cur-&gt;status != THREAD_RUNNING);</span><br><span class="line">  ASSERT (is_thread (next));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (cur != next)</span><br><span class="line">    prev = switch_threads (cur, next);</span><br><span class="line">  thread_schedule_tail (prev);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>schedule</code>函数首先获取当前正在运行的线程指针cur和下一个运行的线程next，之后是三个断言。</p>
<ul>
<li><code>ASSERT (intr_get_level () == INTR_OFF)</code>：保证中断状态是开启的</li>
<li><code>ASSERT (cur-&gt;status != THREAD_RUNNING)</code>：保证当前运行的线程是RUNNING_THREAD的</li>
<li><code>ASSERT (is_thread (next))</code>：保证下一个线程有效</li>
</ul>
<p>17-18行的作用是：如果当前线程和下一个要跑的线程不是同一个的话调用switch_threads返回给prev</p>
<p>下面再看看<code>switch_threads</code>函数(在<code>threads/switch.S</code>中)这是一个完全由汇编语言编写的函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line">#include &quot;threads/switch.h&quot;</span><br><span class="line"></span><br><span class="line">#### struct thread *switch_threads (struct thread *cur, struct thread *next);</span><br><span class="line">####</span><br><span class="line">#### Switches from CUR, which must be the running thread, to NEXT,</span><br><span class="line">#### which must also be running switch_threads(), returning CUR in</span><br><span class="line">#### NEXT&apos;s context.</span><br><span class="line">####</span><br><span class="line">#### This function works by assuming that the thread we&apos;re switching</span><br><span class="line">#### into is also running switch_threads().  Thus, all it has to do is</span><br><span class="line">#### preserve a few registers on the stack, then switch stacks and</span><br><span class="line">#### restore the registers.  As part of switching stacks we record the</span><br><span class="line">#### current stack pointer in CUR&apos;s thread structure.</span><br><span class="line"></span><br><span class="line">.globl switch_threads</span><br><span class="line">.func switch_threads</span><br><span class="line">switch_threads:</span><br><span class="line">	# Save caller&apos;s register state.</span><br><span class="line">	#</span><br><span class="line">	# Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx,</span><br><span class="line">	# but requires us to preserve %ebx, %ebp, %esi, %edi.  See</span><br><span class="line">	# [SysV-ABI-386] pages 3-11 and 3-12 for details.</span><br><span class="line">	#</span><br><span class="line">	# This stack frame must match the one set up by thread_create()</span><br><span class="line">	# in size.</span><br><span class="line">	pushl %ebx</span><br><span class="line">	pushl %ebp</span><br><span class="line">	pushl %esi</span><br><span class="line">	pushl %edi</span><br><span class="line"></span><br><span class="line">	# Get offsetof (struct thread, stack).</span><br><span class="line">.globl thread_stack_ofs</span><br><span class="line">	mov thread_stack_ofs, %edx</span><br><span class="line"></span><br><span class="line">	# Save current stack pointer to old thread&apos;s stack, if any.</span><br><span class="line">	movl SWITCH_CUR(%esp), %eax</span><br><span class="line">	movl %esp, (%eax,%edx,1)</span><br><span class="line"></span><br><span class="line">	# Restore stack pointer from new thread&apos;s stack.</span><br><span class="line">	movl SWITCH_NEXT(%esp), %ecx</span><br><span class="line">	movl (%ecx,%edx,1), %esp</span><br><span class="line"></span><br><span class="line">	# Restore caller&apos;s register state.</span><br><span class="line">	popl %edi</span><br><span class="line">	popl %esi</span><br><span class="line">	popl %ebp</span><br><span class="line">	popl %ebx</span><br><span class="line">        ret</span><br><span class="line">.endfunc</span><br><span class="line"></span><br><span class="line">.globl switch_entry</span><br><span class="line">.func switch_entry</span><br><span class="line">switch_entry:</span><br><span class="line">	# Discard switch_threads() arguments.</span><br><span class="line">	addl $8, %esp</span><br><span class="line"></span><br><span class="line">	# Call thread_schedule_tail(prev).</span><br><span class="line">	pushl %eax</span><br><span class="line">.globl thread_schedule_tail</span><br><span class="line">	call thread_schedule_tail</span><br><span class="line">	addl $4, %esp</span><br><span class="line"></span><br><span class="line">	# Start thread proper.</span><br><span class="line">	ret</span><br><span class="line">.endfunc</span><br></pre></td></tr></table></figure>
<p>分析这段汇编代码，首先将4个寄存器的值压栈保护寄存器状态，这四个寄存器的值是<code>switch_threads_frame</code>的成员，<code>switch_threads_frame</code>结构的具体定义如下(<code>thread/switch.h</code>中定义)：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* switch_thread()'s stack frame. */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">switch_threads_frame</span> </span></span><br><span class="line"><span class="class">  &#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> edi;               <span class="comment">/*  0: Saved %edi. */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> esi;               <span class="comment">/*  4: Saved %esi. */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> ebp;               <span class="comment">/*  8: Saved %ebp. */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> ebx;               <span class="comment">/* 12: Saved %ebx. */</span></span><br><span class="line">    <span class="keyword">void</span> (*eip) (<span class="keyword">void</span>);         <span class="comment">/* 16: Return address. */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">cur</span>;</span>         <span class="comment">/* 20: switch_threads()'s CUR argument. */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">next</span>;</span>        <span class="comment">/* 24: switch_threads()'s NEXT argument. */</span></span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure>
<p>全局变量<code>thread_stack_ofs</code>记录线程和栈之间的间隙，下面我们来看线程切换中保存现场的过程。</p>
<ul>
<li><p>35-36行：先把当前的线程指针放到eax中， 并把线程指针保存在相对基地址偏移量为edx的地址中</p>
</li>
<li><p>40-41: 切换到下一个线程的线程栈指针， 保存在ecx中， 再把这个线程相对基地址偏移量edx地址（上一次保存现场的时候存放的）放到esp当中继续执行。</p>
<blockquote>
<p>这里ecx, eax起容器的作用， edx指向当前现场保存的地址偏移量。简单来说就是保存当前线程状态， 恢复新线程之前保存的线程状态。</p>
</blockquote>
</li>
</ul>
<p>由此我们可以看出<code>schedule</code>函数是先将当前线程放入就绪队列，如果下一个线程和当前线程不一样的话切换到下一个线程。</p>
<p>再看看<code>shcedule</code>函数最后一行执行的操作，最后一行调用<code>thread_schedule_tail</code>函数，下面详细分析一下这个函数（<code>thread/thread.c</code>文件中）。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_schedule_tail</span> <span class="params">(struct thread *prev)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">cur</span> = <span class="title">running_thread</span> ();</span></span><br><span class="line">  </span><br><span class="line">  ASSERT (intr_get_level () == INTR_OFF);</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Mark us as running. */</span></span><br><span class="line">  cur-&gt;status = THREAD_RUNNING;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Start new time slice. */</span></span><br><span class="line">  thread_ticks = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> USERPROG</span></span><br><span class="line">  <span class="comment">/* Activate the new address space. */</span></span><br><span class="line">  process_activate ();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* If the thread we switched from is dying, destroy its struct</span></span><br><span class="line"><span class="comment">     thread.  This must happen late so that thread_exit() doesn't</span></span><br><span class="line"><span class="comment">     pull out the rug under itself.  (We don't free</span></span><br><span class="line"><span class="comment">     initial_thread because its memory was not obtained via</span></span><br><span class="line"><span class="comment">     palloc().) */</span></span><br><span class="line">  <span class="keyword">if</span> (prev != <span class="literal">NULL</span> &amp;&amp; prev-&gt;status == THREAD_DYING &amp;&amp; prev != initial_thread) </span><br><span class="line">    &#123;</span><br><span class="line">      ASSERT (prev != cur);</span><br><span class="line">      palloc_free_page (prev);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>首先是获得当前线程的的cur(切换之后的线程)，然后将cur的状态改为<code>THREAD_RUNNING</code>，然后thread_ticks清零开始新的线程切换时间片。然后调用diaoyong<code>process_activate</code>函数申请新的地址空间，再分析<code>process_active</code>函数(在<code>useruserprog/process.c</code>文件中定义)</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Sets up the CPU for running user code in the current</span></span><br><span class="line"><span class="comment">   thread.</span></span><br><span class="line"><span class="comment">   This function is called on every context switch. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process_activate</span> <span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">thread</span> *<span class="title">t</span> = <span class="title">thread_current</span> ();</span></span><br><span class="line">  <span class="comment">/* Activate thread's page tables. */</span></span><br><span class="line">  pagedir_activate (t-&gt;pagedir);</span><br><span class="line">  <span class="comment">/* Set thread's kernel stack for use in processing</span></span><br><span class="line"><span class="comment">     interrupts. */</span></span><br><span class="line">  tss_update ();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>关键的就是<code>pagedir_activate()</code>函数和<code>tss_update</code>函数，这两个函数分别位于<code>userprog/pagedir.c</code>和<code>userprog/tss.c</code>文件中</p>
<p>下面再进入<code>pagedir_activate()</code>函数中查看。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Loads page directory PD into the CPU's page directory base</span></span><br><span class="line"><span class="comment">   register. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pagedir_activate</span> <span class="params">(<span class="keyword">uint32_t</span> *pd)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (pd == <span class="literal">NULL</span>)</span><br><span class="line">    pd = init_page_dir;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Store the physical address of the page directory into CR3</span></span><br><span class="line"><span class="comment">     aka PDBR (page directory base register).  This activates our</span></span><br><span class="line"><span class="comment">     new page tables immediately.  See [IA32-v2a] "MOV--Move</span></span><br><span class="line"><span class="comment">     to/from Control Registers" and [IA32-v3a] 3.7.5 "Base</span></span><br><span class="line"><span class="comment">     Address of the Page Directory". */</span></span><br><span class="line">  <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"movl %0, %%cr3"</span> : : <span class="string">"r"</span> (vtop (pd)) : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个汇编指令将当前线程的页目录指针存储到CR3（页目录表物理内存基地址寄存器）中，也就是说这个函数更新了现在的页目录表</p>
<p>再进入<code>tss_update</code>函数中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Sets the ring 0 stack pointer in the TSS to point to the end</span></span><br><span class="line"><span class="comment">   of the thread stack. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">tss_update</span> <span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  ASSERT (tss != <span class="literal">NULL</span>);</span><br><span class="line">  tss-&gt;esp0 = (<span class="keyword">uint8_t</span> *) thread_current () + PGSIZE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>tss指的是 task state segment， 叫任务状态段， 任务（进程）切换时的任务现场信息。这里其实是把TSS的一个栈指针指向了当前线程栈的尾部， 也就是更新了任务现场的信息和状态。</p>
<p>到此<code>process_activate</code>函数的分析完毕，它做了两件事：</p>
<ul>
<li>更新页目录表</li>
<li>更新任务现场信息（tss）</li>
</ul>
<p>在继续看<code>thread_schedule_tail</code>函数的最后4行：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (prev != <span class="literal">NULL</span> &amp;&amp; prev-&gt;status == THREAD_DYING &amp;&amp; prev != initial_thread) </span><br><span class="line">&#123;</span><br><span class="line">    ASSERT (prev != cur);</span><br><span class="line">    palloc_free_page (prev);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里是说如果我们切换的线程状态是THREAD_DYING（代表欲要销毁的线程）的话， 调用palloc_free_page（<code>thread/palloc.c</code>文件中定义）：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Frees the page at PAGE. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">palloc_free_page</span> <span class="params">(<span class="keyword">void</span> *page)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  palloc_free_multiple (page, <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>简单而言作用就是释放PAGE参数中的页面</p>
<p>到此，<code>thread_schedule_tail</code>函数分析完毕，其作用就是分配恢复之前执行的状态和现场， 如果当前线程死了就清空资源。</p>
<p><code>schedule</code>函数的作用就是拿下一个线程切换过来继续运行。<code>thread_yield</code>函数的作用是shi把当前进程放在就绪队列里，调用<code>schedule</code>切换到下一个进程。</p>
<p>最后返回到最顶层的<code>timer_sleep</code>函数，他的作用就是在ticks的时间内nei，如果线程处于running状态就不断的把它放在就绪队列不让它执行。</p>

    </div>

    
    
    
        
      
        <div id="reward-container">
  <div></div>
  <button id="reward-button" disable="enable" onclick="var qr = document.getElementById(&quot;qr&quot;); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
        
      
      <div style="display: inline-block">
        <img src="https://ipic-picgo.oss-cn-beijing.aliyuncs.com/20200705000845.png" alt="sunhanwu 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

      

      <footer class="post-footer">
          
            
          
          <div class="post-tags">
            
              <a href="/sunpages/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" rel="tag"># 操作系统</a>
            
          </div>
        

        

          <div class="post-nav">
            <div class="post-nav-next post-nav-item">
              
                <a href="/sunpages/2019/03/11/lab2/" rel="next" title="进程实验">
                  <i class="fa fa-chevron-left"></i> 进程实验
                </a>
              
            </div>

            <span class="post-nav-divider"></span>

            <div class="post-nav-prev post-nav-item">
              
                <a href="/sunpages/2019/04/02/thread/" rel="prev" title="Pintos实验一全解">
                  Pintos实验一全解 <i class="fa fa-chevron-right"></i>
                </a>
              
            </div>
          </div>
        
      </footer>
    
  </div>
  
  
  
  </article>

  </div>


          </div>
          
    
    <div class="comments" id="comments"></div>
  

        </div>
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">
        
        
        
        
      

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#实验三-实验报告"><span class="nav-number">1.</span> <span class="nav-text">实验三 实验报告</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Task-1"><span class="nav-number">1.1.</span> <span class="nav-text">Task 1</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-实验要求"><span class="nav-number">1.1.1.</span> <span class="nav-text">1.1 实验要求</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-实验过程"><span class="nav-number">1.1.2.</span> <span class="nav-text">1.2 实验过程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-实验结果"><span class="nav-number">1.1.3.</span> <span class="nav-text">1.3 实验结果</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-现象解释"><span class="nav-number">1.1.4.</span> <span class="nav-text">1.4 现象解释</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Task-2"><span class="nav-number">1.2.</span> <span class="nav-text">Task 2</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-实验要求"><span class="nav-number">1.2.1.</span> <span class="nav-text">2.1 实验要求</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-实验过程"><span class="nav-number">1.2.2.</span> <span class="nav-text">2.2 实验过程</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-1未添加同步机制"><span class="nav-number">1.2.2.1.</span> <span class="nav-text">2.2.1未添加同步机制</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-2-添加同步机制"><span class="nav-number">1.2.2.2.</span> <span class="nav-text">2.2.2 添加同步机制</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-实验结果"><span class="nav-number">1.2.3.</span> <span class="nav-text">2.3 实验结果</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-现象解释"><span class="nav-number">1.2.4.</span> <span class="nav-text">2.4 现象解释</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-4-1-现象解释1"><span class="nav-number">1.2.4.1.</span> <span class="nav-text">2.4.1 现象解释1</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-4-2-现象解释2"><span class="nav-number">1.2.4.2.</span> <span class="nav-text">2.4.2 现象解释2</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Task-3"><span class="nav-number">1.3.</span> <span class="nav-text">Task 3</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-实验要求"><span class="nav-number">1.3.1.</span> <span class="nav-text">3.1 实验要求</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-实验过程"><span class="nav-number">1.3.2.</span> <span class="nav-text">3.2 实验过程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-实验结果"><span class="nav-number">1.3.3.</span> <span class="nav-text">3.3 实验结果</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-3-1-实验运行现象"><span class="nav-number">1.3.3.1.</span> <span class="nav-text">3.3.1 实验运行现象</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-3-2-实验现象解释"><span class="nav-number">1.3.3.2.</span> <span class="nav-text">3.3.2 实验现象解释</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Task-4"><span class="nav-number">1.4.</span> <span class="nav-text">Task 4</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-实验要求"><span class="nav-number">1.4.1.</span> <span class="nav-text">4.1 实验要求</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-实验过程"><span class="nav-number">1.4.2.</span> <span class="nav-text">4.2 实验过程</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-1-内存共享"><span class="nav-number">1.4.2.1.</span> <span class="nav-text">4.2.1 内存共享</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-2-管道通信"><span class="nav-number">1.4.2.2.</span> <span class="nav-text">4.2.2 管道通信</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#（1）无名管道"><span class="nav-number">1.4.2.2.1.</span> <span class="nav-text">（1）无名管道</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#（2）有名管道"><span class="nav-number">1.4.2.2.2.</span> <span class="nav-text">（2）有名管道</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-3-消息队列"><span class="nav-number">1.4.2.3.</span> <span class="nav-text">4.2.3 消息队列</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Task-5"><span class="nav-number">1.5.</span> <span class="nav-text">Task 5</span></a></li></ol></li></ol></div>
        
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
      src="http://ipic-picgo.oss-cn-beijing.aliyuncs.com/2019-09-12-logo.jpg"
      alt="sunhanwu">
  <p class="site-author-name" itemprop="name">sunhanwu</p>
  <div class="site-description" itemprop="description">Talk is cheap, show me the code!</div>
</div>
  <nav class="site-state motion-element">
      <div class="site-state-item site-state-posts">
        
          <a href="/sunpages/archives/%20%7C%7C%20archive">
        
          <span class="site-state-item-count">29</span>
          <span class="site-state-item-name">博客</span>
        </a>
      </div>
    
      
      
      <div class="site-state-item site-state-categories">
        
          
            <a href="/sunpages/categories/%20%7C%7C%20th">
          
        
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span>
        </a>
      </div>
    
      
      
      <div class="site-state-item site-state-tags">
        
          
            <a href="/sunpages/tags/%20%7C%7C%20tags">
          
        
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span>
        </a>
      </div>
    
  </nav>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="https://github.com/sunhanwu" title="GitHub &rarr; https://github.com/sunhanwu" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
      </span>
    
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="mailto:1965190613@qq.com" title="E-Mail &rarr; mailto:1965190613@qq.com" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
      </span>
    
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title">
      <i class="fa fa-fw fa-link"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://jackyanghc.github.io/" title="https://jackyanghc.github.io/" rel="noopener" target="_blank">Jackyanghc</a>
        </li>
      
        <li class="links-of-blogroll-item">
          <a href="https://michel-liu.github.io/" title="https://michel-liu.github.io/" rel="noopener" target="_blank">Michel</a>
        </li>
      
        <li class="links-of-blogroll-item">
          <a href="https://www.cnblogs.com/morwing/" title="https://www.cnblogs.com/morwing/" rel="noopener" target="_blank">morwing</a>
        </li>
      
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">sunhanwu</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
      <span class="post-meta-item-text">站点总字数：</span>
    
    <span title="站点总字数">161k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span class="post-meta-item-text">站点阅读时长 &asymp;</span>
    
    <span title="站点阅读时长">2:26</span>
</div>
  <div class="theme-info">主题 – <a href="https://theme-next.org" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> v7.4.0</div>

        












        
      </div>
    </footer>
  </div>

  


  <script src="/sunpages/lib/anime.min.js?v=3.1.0"></script>
  <script src="/sunpages/lib/velocity/velocity.min.js?v=1.2.1"></script>
  <script src="/sunpages/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

<script src="/sunpages/js/utils.js?v=7.4.0.js"></script>

<script src="/sunpages/js/motion.js?v=7.4.0.js"></script>


<script src="/sunpages/js/schemes/pisces.js?v=7.4.0.js"></script>


<script src="/sunpages/js/next-boot.js?v=7.4.0.js"></script>




  
  <script>
    (function(){
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>








  
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/instantsearch.js@2/dist/instantsearch.min.css">
<script src="//cdn.jsdelivr.net/npm/instantsearch.js@2/dist/instantsearch.min.js"></script>
<script src="/sunpages/js/algolia-search.js?v=7.4.0.js"></script>
















  

  

  

  


<script>
NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
  var GUEST = ['nick', 'mail', 'link'];
  var guest = 'nick,mail,link';
  guest = guest.split(',').filter(item => {
    return GUEST.includes(item);
  });
  new Valine({
    el: '#comments',
    verify: false,
    notify: false,
    appId: 'ToSqcWxrMe4Fx1bTFKjHnFQn-gzGzoHsz',
    appKey: 'WIlMiWIudPsl2Q2PsMVfiUUH',
    placeholder: 'Just go go',
    avatar: 'mm',
    meta: guest,
    pageSize: '10' || 10,
    visitor: false,
    lang: '' || 'zh-cn',
    path: location.pathname
  });
}, window.Valine);
</script><!-- hexo-inject:begin --><!-- Begin: Injected MathJax -->
<script type="text/x-mathjax-config">
  MathJax.Hub.Config("");
</script>

<script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
      all[i].SourceElement().parentNode.className += ' has-jax';
    }
  });
</script>

<script type="text/javascript" src="">
</script>
<!-- End: Injected MathJax -->
<!-- hexo-inject:end -->

</body>
</html>
